package com.warren.auth.config;

import com.warren.auth.entity.JwtUser;
import com.warren.auth.filter.JWTAuthenticationFilter;
import com.warren.auth.handler.*;

import com.warren.auth.utils.MyAccessDecisionVoter;
import com.warren.auth.utils.MySecurityMetadataSource;
import com.warren.constants.SecurityContants;
import com.warren.system.entity.User;
import com.warren.system.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;

import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author warren
 * @date 2021/3/24 0024 15:22
 * @Description
 **/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private UserService userService;

    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;


    @Bean
    public PasswordEncoder passwordEncoder(){
        //使用这么一个实现类
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private MyAccessDecisionVoter myAccessDecisionVoter;

    @Autowired
    private MySecurityMetadataSource mySecurityMetadataSource;

    /**
     * 失败的处理方案
     */
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    /**
     * 成功的处理方案
     */
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    /**
     * 账号被挤下线处理方式
     */
    @Autowired
    private MyInvalidSessionStrategy myInvalidSessionStrategy;


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置自定义的userDetailService以及密码编辑器
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/login",
                "/logout",
                "/css/**",
                "/js/**",
                "/index.html",
                "favicon.ico",
                "/doc.html",
                "/webjars/**",
                "/swagger-resources/**",
                "/v2/api-docs/**",
                "/captcha2",
                "/ws/**",
                "/api/limit/test"
        );
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭csrf跨域
        http.cors().
                and()
                .csrf().disable()
                .authorizeRequests()
                //指定的接口放行
                .antMatchers("/","/main","/403").permitAll()
                //swagger
                .antMatchers(SecurityContants.SWAGGER_WHITELIST).permitAll()
                .antMatchers(SecurityContants.KNIFE4J_WHITELIST).permitAll()
                .antMatchers(HttpMethod.POST,SecurityContants.SYSTEM_WHITELIST).permitAll()
                //其他接口都需要认证后才能请求
                .anyRequest()
                //.access("@rabcService.hasPermission(request,authentication)")
                .authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(myAccessDecisionVoter);
                        object.setSecurityMetadataSource(mySecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                //添加自定义Filter
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                //不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //授权异常处理
                .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
                .accessDeniedHandler(new JWTAccessDeniedHandler())
                //退出登录
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(myLogoutSuccessHandler);
                //授权失败
                /*.and()
                .sessionManagement()
                //允许最大登录数量
                .maximumSessions(2)
                //是否允许另一个账户登录
                .maxSessionsPreventsLogin(true)
                //被挤下线的处理方式
                .expiredSessionStrategy(myInvalidSessionStrategy);
*/
    }

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            User user = userService.getAdminByUserName(username);
            JwtUser userInfo = new JwtUser(user);
            userInfo.setRoles(userService.getRole(user.getId()));
            return userInfo;
        };
    }

    @Bean
    public JWTAuthenticationFilter jwtAuthenticationFilter(){
        return new JWTAuthenticationFilter();
    }



}
